﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using PropertyExpression.Common;

namespace PropertyExpression.ControllableQuery.Test
{
    public static class QExecuter
    {
        public static int Execute(this INonQuery it)
        {
            SqlCommand command;
            return it.Execute(out command);
        }

        public static int Execute(this INonQuery it, out SqlCommand command)
        {
            using (var connection = new SqlConnection(QueryTest.ConnectionString))
            {
                connection.Open();
                using (command = connection.CreateCommand())
                {
                    command.SetQuery(it);
                    return command.ExecuteNonQuery();
                }
            }
        }

        public static T Single<T>(this IQuery<Type<T>> it)
        {
            return it.Result().Single();
        }

        public static List<T> List<T>(this IQuery<Type<T>> it)
        {
            return it.Result().ToList();
        }

        public static List<T> List<T>(this IQuery<Type<T>> it, out SqlCommand command)
        {
            using (var connection = new SqlConnection(QueryTest.ConnectionString))
            {
                connection.Open();
                using (command = connection.CreateCommand())
                {
                    command.SetQuery(it);
                    return command.ExecuteEnumerable(it).ToList();
                }
            }
        }

// ReSharper disable UnusedParameter.Local
        private static IEnumerable<T> ExecuteEnumerable<T>(this SqlCommand it, IQuery<Type<T>> query)
// ReSharper restore UnusedParameter.Local
        {
            using (var reader = it.ExecuteReader())
                while (reader.Read())
                    yield return reader.Materialize<T>();
        }

        public static IEnumerable<T> Result<T>(this IQuery<Type<T>> it)
        {
            using (var connection = new SqlConnection(QueryTest.ConnectionString))
            {
                connection.Open();
                using (var command = connection.CreateCommand())
                {
                    command.SetQuery(it);
                    foreach (var result in command.ExecuteEnumerable(it)) yield return result;
                }
            }
        }

        public static Paging<List<T1>, int> Result<T1>(this IQuery<Paging<T1, int?>> it)
        {
            return it.Result((List<T1> item1, List<int?> item2) =>
                new Paging<List<T1>, int>(item1, item2.Single().Value));
        }

        public static Tuple<List<T1>, List<T2>> Result<T1, T2>(this IQuery<Type<T1, T2>> it)
        {
            return it.Result((List<T1> item1, List<T2> item2) => Tuple.Create(item1, item2));
        }

        private static TResult Result<T1, T2, TResult>(this IQuery it, Func<List<T1>, List<T2>, TResult> func)
        {
            return it.Result(reader => {
                var item1 = reader.ToEnumerable<T1>().ToList();
                reader.NextResult();
                var item2 = reader.ToEnumerable<T2>().ToList();
                return func(item1, item2);
            });
        }

        public static Tuple<List<T1>, List<T2>, List<T3>> Result<T1, T2, T3>(this IQuery<Type<T1, T2, T3>> it)
        {
            return it.Result(reader => {
                var item1 = reader.ToEnumerable<T1>().ToList();
                reader.NextResult();
                var item2 = reader.ToEnumerable<T2>().ToList();
                reader.NextResult();
                var item3 = reader.ToEnumerable<T3>().ToList();
                return Tuple.Create(item1, item2, item3);
            });
        }

        private static TResult Result<TResult>(this IQuery it, Func<SqlDataReader, TResult> func)
        {
            using (var connection = new SqlConnection(QueryTest.ConnectionString))
            {
                connection.Open();
                using (var command = new SqlCommand())
                {
                    command.Connection = connection;
                    command.SetQuery(it);
                    using (var sqlDataReader = command.ExecuteReader())
                        return func(sqlDataReader);
                }
            }
        }

        private static IEnumerable<T> ToEnumerable<T>(this SqlDataReader reader)
        {
            while (reader.Read()) 
                yield return reader.Materialize<T>();
        }

        //TODO: расскоментировать или удалить
        //public static Tuple<List<T1>, List<T2>> GetResult<T1, T2>(this IQuery<T1, T2> @this)
        //{
        //    throw new System.NotImplementedException();
        //}

        //public static T GetResult<T>(this IScalarQuery<T> @this)
        //{
        //    throw new System.NotImplementedException();
        //}

        //public static List<T> GetList<T>(this IQuery<T> query)
        //{
        //    return GetEnumerable(query).ToList();
        //}

        //public static IEnumerable<T> GetEnumerable<T>(this IQuery<T> query)
        //{
        //    using (var connection = new SqlConnection(QueryTest.ConnectionString))
        //    {
        //        connection.Open();
        //        using (var command = new SqlCommand())
        //        {
        //            command.Connection = connection;
        //            command.CommandText = query.CommandText;
        //            foreach (var sqlParameter in query.SqlParameters) command.Parameters.Add(sqlParameter);
        //            using (var sqlDataReader = command.ExecuteReader())
        //                while (sqlDataReader.Read())
        //                    yield return query.GetReader(sqlDataReader).Materialize();
        //        }
        //    }
        //}

    }
}